/*
 * Stepper.java
 *
 * Copyright(c) 2010 - Kimnach
 * See main.Main.java for full copyright notice.
 *
 */

package sequence;

import exceptions.StepNotFoundException;
import java.util.HashMap;
import java.util.Stack;

/**
 * Handles stepping through each of the gameplay steps
 * @author dkimnach
 */
public class Stepper {

    private Stack<String> history = new Stack<String>();
    private HashMap<String, Step> game = new HashMap<String, Step>();
    private Step current;
    private Boolean monsterDead = false;
    private Boolean allDead = false;
    private int encounters = 0; // tracks encounters
    
    public final static String BLANK = "blank";
    public final static String BEGIN = "begin";
    public final static String END = "end";
    public final static String ENC = "encounter";
    public final static String POT = "potions";
    public final static String ENC_INIT = "enc_init";
    public final static String DISC_DRAW = "disc_draw";
    public final static String PRES = "prestige";
    public final static String ENC_BEAD = "enc_bead";
    public final static String SET = "set";
    public final static String PLAY = "play";
    public final static String DMG_BEAD = "dmg_bead";
    public final static String MON_ATT = "mon_attacks";
    public final static String ROU_INIT = "round_init";
    public final static String INIT_ATT = "init_attacks";
    public final static String DRAW_ONE = "draw_one";
    
    /**
     * Constructs an instance of <code>Stepper</code> 
     */
    public Stepper() {
        game.put(BLANK,     new Step(BLANK,     BEGIN,      " ",                0));
        game.put(BEGIN,     new Step(BEGIN,     ENC,        "New Game Setup",   1));
        initEncounter();    // setup encounter
        initRound();        // encounter attack rounds
        initReward();       // end of encounter
        game.put(END,       new Step(END,       BEGIN,      "End of Game",      -1));
        
        current = game.get(BEGIN);
    }

    private void initEncounter() {
        // encounter loop
        game.put(DISC_DRAW, new Step(DISC_DRAW,     ENC,        "Discard & Draw (init order)",  2));
        game.put(ENC,       new Step(ENC,           POT,        "New Encounter",                3));
        game.put(POT,       new Step(POT,           ENC_INIT,   "Tap Potions",                  4));
        game.put(ENC_INIT,  new Step(ENC_INIT,      SET,        "Deal First Initiatives",       5));
    }

    private void initReward() {
        game.put(PRES,      new Step(PRES,          ENC_BEAD,   "Award Prestige",       13));
        game.put(ENC_BEAD,  new Step(ENC_BEAD,      DISC_DRAW,  "Move Encounter Bead",  14));
    }

    private void initRound() {
        // endless loop until monster dies
        game.put(SET,       new Step(SET,           PLAY,       "Set Attacks",                      6));
        game.put(PLAY,      new Step(PLAY,          DMG_BEAD,   "Play Attacks (init order)",        7));
        game.put(DMG_BEAD,  new Step(DMG_BEAD,      MON_ATT,    "Damage Monster",                   8));
        game.put(MON_ATT,   new Step(MON_ATT,       ROU_INIT,   "Damage-based Monster Attacks",     9));
        game.put(ROU_INIT,  new Step(ROU_INIT,      INIT_ATT,   "Deal Initiatives",                 10));
        game.put(INIT_ATT,  new Step(INIT_ATT,      DRAW_ONE,   "Initiative-based Monster Attacks", 11));
        game.put(DRAW_ONE,  new Step(DRAW_ONE,      SET,        "Draw One (init order)",            12));
    }

    /**
     * Advances the game one step
     * @return next step
     * @throws StepNotFoundException
     */
    public Step next() throws StepNotFoundException {
        Step s;

        if (monsterDead) { // check to see if the round ends (when monster dies)
            monsterDead = false;
            s = goTo(PRES);
        } else {
            s = goTo(current.next());   // go to next
        }

        if (s.getName().equals(Stepper.ENC)) {
            encounters++;
        }

        return s;
    }

    /**
     * Goes back one step
     * @return last step
     * @throws StepNotFoundException
     */
    public Step last() throws StepNotFoundException {
        Step s;

        if (current.getName().equals(Stepper.ENC)) {
            encounters--;
        }

        if (!history.empty()) {
            s = game.get(history.pop());
            current = s;
        } else  {
            throw new StepNotFoundException("ERROR: history empty. cannot go back a step");
        }

        return s;
    }

    /**
     * Resets position and history, reusing instance rather than create new one
     * @return beginning step
     */
    public Step newGame() {
        history.clear();
        current = game.get(BEGIN);
        
        return current;
    }

    /**
     * Advance the game to [step]
     * @param step
     * @return current step
     * @throws StepNotFoundException thrown if [step] is an invalid step
     */
    public Step goTo(String step) throws StepNotFoundException {
        if (game.containsKey(step)) {
            history.push(current.getName()); // save current
            current = game.get(step); // new current
        } else {
            throw new StepNotFoundException("ERROR: step not found. Cannot go to step '" + step + "'");
        }

        return current;
    }

    /**
     * Monster health reached 0
     */
    public void monsterDies() {
        monsterDead = true;
    }
    
    /**
     * Returns current step
     * @return current
     */
    public Step getStep() {
        return current;
    }

    /**
     * Get Step information using string key (does not advance game)
     * @param step Name identifying step
     * @return step retrieved
     */
    public Step getStep(String step) throws StepNotFoundException {
        Step s;

        if (game.containsKey(step)) {
            s = game.get(step);
        } else {
            s = current;
            throw new StepNotFoundException("ERROR: step not found. cannot go to step '" + step + "'");
        }

        return s;
    }

    /**
     * Get Last Step from history (does not remove it)
     * @return step retrieved
     */
    public Step getLast() {
        Step last;
        if (history.empty()) {
            last = game.get(BLANK);
        } else {
            last = game.get(history.peek());
        }

        return last;
    }

    public int getEncounters() {
        return encounters;
    }
}